#
# Copyright (c) 2013 Juniper Networks, Inc. All rights reserved.
#

import platform
if platform.system() == 'Darwin':
    Return()

import time
import subprocess

Import('BuildEnv')
env = BuildEnv.Clone()

##########################################################################
# Build CCFLAGS
##########################################################################
env.CppDisableExceptions()
if GetOption('with_libvirt'):
    env.Append(CCFLAGS = ['-DWITH_LIBVIRT'])

##########################################################################
# Build CPPPATH
##########################################################################
env.Prepend(CPPPATH = '#/vrouter/include')
if platform.system() == 'Windows':
    env.Prepend(CPPPATH = '#/vrouter/windows')
env.Prepend(CPPPATH = env['TOP_INCLUDE'] + '/thrift')
env.Prepend(CPPPATH = env['TOP'] + '/tools/sandesh/library/common')

# Function to append TOP level directory
def AddTopDir(list):
    return [Dir(env['TOP'] + '/' + x).abspath for x in list]

cpppath = [
    '',
    'base/sandesh',
    'dns',
    'http/client',
    'ifmap',
    'schema',
    'sandesh',
    'vnsw/agent',
    'vnsw/agent/oper',
    'vnsw/agent/cmn',
    'vnsw/agent/uve',
    'vrouter/sandesh',
    'vnsw/agent/nexthop_server',
]

if platform.system() == 'Linux':
    cpppath.append('vnsw/agent/vrouter/ksync/linux')
elif platform.system() == 'FreeBSD':
    cpppath.append('vnsw/agent/vrouter/ksync/freebsd')
elif platform.system() == 'Windows':
    cpppath.append('vnsw/agent/vrouter/ksync/windows')

env.Prepend(CPPPATH = AddTopDir(cpppath))


##########################################################################
# Build LIBPATH
##########################################################################
def MapBuildDir(list):
    return [Dir('../../' + x).abspath for x in list]

env.Append(LIBPATH = MapBuildDir([
    'base',
    'bfd',
    'bgp',
    'db',
    'dns/bind',
    'http/client',
    'ifmap',
    'io',
    'ksync',
    'net',
    'route',
    'schema',
    'vnsw/agent',
    'vnsw/agent/cfg',
    'vnsw/agent/cmn',
    'vnsw/agent/controller',
    'vnsw/agent/diag',
    'vnsw/agent/filter',
    'vnsw/agent/init',
    'vnsw/agent/kstate', 
    'vnsw/agent/mac_learning',
    'vnsw/agent/port_ipc',
    'vnsw/agent/oper',
    'vnsw/agent/pkt',
    'vnsw/agent/resource_manager',
    'vnsw/agent/services',
    'vnsw/agent/services/multicast/gmp_map',
    'vnsw/agent/services/multicast/stubs',
    'vnsw/agent/services/multicast/grpmgmt',
    'vnsw/agent/services/multicast/agent_map',
    'vnsw/agent/uve',
    'vnsw/agent/vgw',
    'vnsw/agent/vrouter/flow_stats',
    'vnsw/agent/vrouter/ksync',
    'vnsw/agent/vrouter/stats_collector',
    'vrouter/utils',
    'xml',
    'xmpp',
    'vnsw/agent/nexthop_server',
]))

##########################################################################
# Build LIBS - Libraries to be linked
##########################################################################

# Common non-agent libraries
env.Prepend(LIBS = [
    'bfd_udp',
    'bfd',
    'xmpp',
    'peer_sandesh',
    'xmpp_multicast',
    'xmpp_mvpn',
    'xmpp_enet',
    'xmpp_unicast',
    'xml2',
    'xml',
    'pugixml',
    'curl',
    'bind_interface',
    'bgp_schema',
    'ifmap_vnc',
    'ifmap_agent',
    'ifmap_common', 
    'ifmapio',
    'net',
    'sandeshflow',
    'sandeshvns',
    'filter',
    'httpc',
    'route',
    'db',
    'cpuinfo',
    'io',
    'ssl',
    'crypto',
    'sandesh',
    'nodeinfo',
    'cpuinfo',
    'process_info',
    'http',
    'http_parser',
    'base',
    'thriftasio',
    'thrift',
    'boost_filesystem',
    'boost_program_options',
    'boost_regex',
])

if platform.system() == 'Windows':
    env.Prepend(LIBS = [
        'gdi32',
        'iphlpapi',
    ])
else:
    env.Prepend(LIBS = 'rt')

# Libvirt library
if GetOption('with_libvirt'):
    env.Prepend(LIBS = ['virt'])

# Common agent libraries
env.Prepend(LIBS = [
    'vnswctrl',
    'cfg',
    'filter',
    'ksync',
    'resource_manager',
    'uve',
    'vgw',
    'vnswnhserver',
    'mac_learning'
])

##########################################################################
# Build LINKFLAGS - libraries needing multi-pass linking
##########################################################################
AGENT_MULTIPATH_LIBS = [
    'cpuinfo',
    'port_ipc',
    'vnswoperdb',
    'vnswcmn',
    'resource_manager'
]

# Set LIBS environment to ensure libraries are built
env.Prepend(LIBS = AGENT_MULTIPATH_LIBS)

def MakeLinkCmd(list):
    return map(lambda x: '-l' + x, list)

if platform.system() != 'Windows':
    env.Prepend(LINKFLAGS = [ '-Wl,--whole-archive', '-rdynamic'] +
                              MakeLinkCmd(AGENT_MULTIPATH_LIBS) +
                            ['-Wl,--no-whole-archive'])
# Prefer linking against the self-built libraries over the system ones
env.AppendENVPath('LD_RUN_PATH', '#/build/lib')

##########################################################################
# Build AgentEnv variables used by sub-directores
##########################################################################
AgentEnv = env.Clone()

# Contrail agent libraries
AgentEnv.AppendUnique(CONTRAIL_LIBS = [
    'contrail_init_common',
    'agent_services',
    'gmp_map',
    'grpmgmt',
    'gmp_utils',
    'agent_map',
    'diag',
    'flowstats',
    'pkt',
    'statsuve',
    'statscollector',
    'vnswksync',
    'ksyncnl',
    'kstate',
    'vrutil',
])

if platform.system() != 'Windows':
    AgentEnv.AppendUnique(CONTRAIL_LIBS = [
        'tcmalloc',
        'isc',
    ])

# Common libraries for agent test code
AgentEnv.AppendUnique(TEST_CMN_LIBS_PRE = [
    'agent_test',
    'ifmap_test',
    'task_test',
    'control_node_mock',
    'gunit',
])

AgentEnv.AppendUnique(TEST_CMN_LIBS_POST = [ 'agent_test_buildinfo' ])
AgentEnv.AppendUnique(TEST_CMN_CPPPATH = [env['TOP'] + '/vnsw/agent/test'])
AgentEnv.AppendUnique(TEST_CMN_LIBPATH = [
    env['TOP'] + '/base/test',
    env['TOP'] + '/vnsw/agent/test',
])

if platform.system() == 'Windows':
    AgentEnv['TEST_CMN_CCFLAGS'] = []
else:
    AgentEnv.AppendUnique(TEST_CMN_CCFLAGS = [
        '-Wno-return-type',
        '-Wno-unused-function'
    ])

##########################################################################
# Utility functions
##########################################################################
def RemoveExceptionFlagFn(self, env):
    except_env = env.Clone()
    cflags = except_env['CCFLAGS']
    if '-fno-exceptions' in cflags:
        cflags.remove('-fno-exceptions')
        except_env.Replace(CCFLAGS = cflags)
    return except_env

def BuildExceptionCppObjFn(self, env, file_list):
    except_env = env.Clone()
    cflags = except_env['CCFLAGS']
    if '-fno-exceptions' in cflags:
        cflags.remove('-fno-exceptions')
        except_env.Replace(CCFLAGS = cflags)
    obj_list = []
    for src in file_list:
        objname = src.replace('.cpp', '.o')
        obj = except_env.Object(objname, src)
        obj_list.append(obj)
    return obj_list

def BuildExceptionCcObjFn(self, env, file_list):
    except_env = env.Clone()
    cflags = except_env['CCFLAGS']
    if '-fno-exceptions' in cflags:
        cflags.remove('-fno-exceptions')
        except_env.Replace(CCFLAGS = cflags)
    obj_list = []
    for src in file_list:
        objname = src.replace('.cc', '.o')
        obj = except_env.Object(objname, src)
        obj_list.append(obj)
    return obj_list

def MakeTestEnvFn(self, env):
    env.Append(CPPPATH = AgentEnv['TEST_CMN_CPPPATH'])
    env.Append(LIBPATH = AgentEnv['TEST_CMN_LIBPATH'])
    env.Append(CCFLAGS = AgentEnv['TEST_CMN_CCFLAGS'])
    env.Prepend(LIBS = AgentEnv['CONTRAIL_LIBS'])
    env.Prepend(LIBS = AgentEnv['TEST_CMN_LIBS_PRE'])
    env.Append(LIBS = AgentEnv['TEST_CMN_LIBS_POST'])
    cflags = env['CCFLAGS']
    if '-fno-exceptions' in cflags:
        cflags.remove('-fno-exceptions')
        env.Replace(CCFLAGS = cflags)

def MakeTestCmdWithSrcFn(self, env, test_name, src, test_list):
    tgt = env.UnitTest(target = test_name, source = src)
    env.Alias('agent:'+ test_name, tgt)
    test_list.append(tgt)
    return tgt

def MakeTestCmdFn(self, env, test_name, test_list):
    tgt = env.UnitTest(target = test_name, source = [test_name + '.cc'])
    env.Alias('agent:'+ test_name, tgt)
    test_list.append(tgt)
    return tgt

AgentEnv.AddMethod(MakeTestEnvFn, 'MakeTestEnv')
AgentEnv.AddMethod(MakeTestCmdFn, 'MakeTestCmd')
AgentEnv.AddMethod(MakeTestCmdWithSrcFn, 'MakeTestCmdSrc')
AgentEnv.AddMethod(BuildExceptionCppObjFn, 'BuildExceptionCppObj')
AgentEnv.AddMethod(BuildExceptionCcObjFn, 'BuildExceptionCcObj')
AgentEnv.AddMethod(RemoveExceptionFlagFn, 'RemoveExceptionFlag')

Export('AgentEnv')

##########################################################################
# Sub-directories to build
##########################################################################
subdirs = [
    'cmn',
    'cfg',
    'controller',
    'diag',
    'filter',
    'init',
    'kstate',
    'mac_learning',
    'oper',
    'pkt',
    'port_ipc',
    'resource_manager',
    'services',
    'test',
    'uve',
    'vgw',
    'vrouter',
    'nexthop_server',
]

if platform.system() != 'Windows':
    subdirs += [
        'ovs_tor_agent',
    ]

AgentUveDocFiles = []
AgentUveDocFiles += AgentEnv.SandeshGenDoc('uve/vrouter.sandesh')
AgentUveDocFiles += AgentEnv.SandeshGenDoc('uve/prouter.sandesh')
AgentUveDocFiles += AgentEnv.SandeshGenDoc('uve/loadbalancer.sandesh')
AgentUveDocFiles += AgentEnv.SandeshGenDoc('uve/virtual_machine.sandesh')
AgentUveDocFiles += AgentEnv.SandeshGenDoc('uve/virtual_network.sandesh')
AgentUveDocFiles += AgentEnv.SandeshGenDoc('uve/port_bmap.sandesh')
AgentUveDocFiles += AgentEnv.SandeshGenDoc('uve/interface.sandesh')
AgentUveDocFiles += AgentEnv.SandeshGenDoc('uve/stats_interval.sandesh')
AgentEnv['AGENT_UVE_DOC_FILES'] = AgentUveDocFiles

AgentKsyncDocFiles = []
AgentKsyncDocFiles += AgentEnv['KSYNC_DOC_FILES']
AgentKsyncDocFiles += AgentEnv.SandeshGenDoc('kstate/kstate.sandesh')
AgentKsyncDocFiles += AgentEnv.SandeshGenDoc('vrouter/ksync/agent_ksync.sandesh')
AgentEnv['AGENT_KSYNC_DOC_FILES'] = AgentKsyncDocFiles

AgentFlowDocFiles = []
AgentFlowDocFiles += AgentEnv.SandeshGenDoc('#controller/src/sandesh/common/flow.sandesh', env['TOP'] + '/sandesh/common/')
AgentFlowDocFiles += AgentEnv.SandeshGenDoc('vrouter/flow_stats/flow_stats.sandesh')
AgentEnv['AGENT_FLOW_DOC_FILES'] = AgentFlowDocFiles

AgentStatsCollectorDocFiles = []
AgentStatsCollectorDocFiles += AgentEnv.SandeshGenDoc('vrouter/stats_collector/agent_stats_interval.sandesh')
AgentEnv['AGENT_STATS_COLLECTOR_DOC_FILES'] = AgentStatsCollectorDocFiles

AgentCommonDocFiles = []
AgentCommonDocFiles += AgentEnv.SandeshGenDoc('port_ipc/port_ipc.sandesh')
AgentCommonDocFiles += AgentEnv.SandeshGenDoc('diag/diag.sandesh')
AgentCommonDocFiles += AgentEnv.SandeshGenDoc('cmn/stats.sandesh')
AgentCommonDocFiles += AgentEnv.SandeshGenDoc('controller/controller.sandesh')
AgentCommonDocFiles += AgentEnv.SandeshGenDoc('cfg/cfg.sandesh')
AgentCommonDocFiles += AgentEnv.SandeshGenDoc('oper/multicast.sandesh')
AgentCommonDocFiles += AgentEnv.SandeshGenDoc('oper/agent.sandesh')
AgentCommonDocFiles += AgentEnv.SandeshGenDoc('services/services.sandesh')
AgentCommonDocFiles += AgentEnv.SandeshGenDoc('pkt/pkt.sandesh')
AgentCommonDocFiles += AgentEnv.SandeshGenDoc('mac_learning/mac_learning.sandesh')
AgentCommonDocFiles += AgentEnv.SandeshGenDoc('oper/agent_profile.sandesh')
AgentCommonDocFiles += AgentEnv['BASE_DOC_FILES']
AgentCommonDocFiles += AgentEnv['IO_DOC_FILES']
AgentCommonDocFiles += AgentEnv['DB_DOC_FILES']
AgentCommonDocFiles += AgentEnv['IFMAP_CLIENT_DOC_FILES']
AgentCommonDocFiles += AgentEnv['XMPP_DOC_FILES']
AgentCommonDocFiles += AgentEnv['SANDESH_DOC_FILES']
AgentEnv['AGENT_COMMON_DOC_FILES'] = AgentCommonDocFiles

AgentOvsDocFiles = []
AgentOvsDocFiles += AgentEnv.SandeshGenDoc('ovs_tor_agent/ovsdb_client/ovsdb.sandesh')
AgentEnv['AGENT_OVS_DOC_FILES'] = AgentOvsDocFiles

for dir in subdirs:
    env.SConscript(dir + '/SConscript', exports=['AgentEnv', 'BuildEnv'],
                   duplicate=0)

env.SConscript('contrail/SConscript', exports=['AgentEnv', 'BuildEnv'],
               duplicate=0)

# TODO(md): Currently not supported on FreeBSD
if platform.system() != 'FreeBSD':
    env.SConscript('vxlan_agent/SConscript', exports=['AgentEnv', 'BuildEnv'],
                   duplicate=0)

env.SConscript('test-xml/SConscript', exports=['AgentEnv', 'BuildEnv'],
               duplicate=0)

##########################################################################
# Utility functions
##########################################################################
#cflags = AgentEnv['CCFLAGS']
#cflags.remove('-Wno-return-type')
#AgentEnv.Replace(CCFLAGS = cflags)
#Export('AgentEnv')

def valgrind_run(target, source, env):
    #import pdb; pdb.set_trace()

    #execute tests with valgrind
    import subprocess
    ShEnv = {env['ENV_SHLIB_PATH']: 'build/lib'}

    import os
    # Get test alias from agent-test test suite
    for test in env.ans['agent-test'].sources:
        cmd = test.sources[0].path
        logfile = open(cmd + '.log', 'w')
        valgrindfile = '--log-file=' + cmd + '.valgrind'
        supp_file = '--suppressions=' + 'src/vnsw/agent/test/valgrind.supp'
        print(cmd + ' logfile=' + cmd + '.log' + ' valgrind_file=' + cmd + '.valgrind ')
        code = subprocess.call(['valgrind', '-v', '--track-origins=yes',
                                '--leak-check=full', '--error-exitcode=1',
                                '--show-reachable=yes', supp_file, valgrindfile,
                                cmd], stdout=logfile, stderr=logfile, env=ShEnv)
        if code == 0:
            print(cmd + '\033[94m' + " PASS" + '\033[0m')
        else:
            if code < 0:
                logfile.write('Terminated by signal: ' + str(-code) + '\n')
            print(cmd + '\033[91m' + " FAIL" + '\033[0m')

#test_valgrind = env.Command(Dir('test_valgrind'), '', valgrind_run)
#env.Alias('agent:test_valgrind', test_valgrind)

def code_coverage(target, source, env):
    # rm -rf vnsw/agent/test_coverage
    import shutil
    #import pdb; pdb.set_trace()
    shutil.rmtree(target[0].path, ignore_errors = True)

    # lcov --base-directory $ROOT -- directory . --zerocounters -q
    import os
    os.system('lcov --base-directory . --directory ' + Dir('.').path +
              ' --zerocounters -q')
    # execute tests
    import subprocess
    #import pdb; pdb.set_trace()
    ShEnv = {env['ENV_SHLIB_PATH']: 'build/lib'}

    # Get test alias from agent-test test suite 
    for test in env.ans['agent-test'].sources:
        cmd = test.sources[0].path
        print(cmd)
        logfile = open(cmd + '.log', 'w')
        subprocess.call([cmd], stdout=logfile, stderr=logfile, env=ShEnv)

    # lcov --base-directory $ROOT -- directory . -c -o bgp_test.info
    os.system('lcov --base-directory . --directory ' + Dir('.').path +
              ' -c --ignore-errors gcov -o agent_test.info')

    # genhtml -o bgp/test_coverage bgp_test.info
    os.system('genhtml -o ' + target[0].path +
              ' -t "test coverage" --num-spaces 4 agent_test.info')


if env['OPT'] == 'coverage':
    #import pdb; pdb.set_trace()
    test_coverage = env.Command(Dir('test_coverage'), '', code_coverage)
    env.AlwaysBuild(test_coverage)
    env.Alias('agent:test_coverage', test_coverage)

def run_ut(target, source, env):
    # execute tests
    import subprocess
    #import pdb; pdb.set_trace()
    ShEnv = {env['ENV_SHLIB_PATH']: 'build/lib'}

    # Get test alias from agent-test test suite 
    for test in env.ans['agent-test'].sources:
        cmd = test.sources[0].path
        log = test.sources[0].path + '.log'
        logfile = open(cmd + '.log', 'w')
        proc = subprocess.Popen(cmd, stdout=logfile, stderr=logfile, env=ShEnv)

        # 60 second timeout
        timeout = 60
        for i in range(timeout):
            code = proc.poll()
            if not code is None:
                break
            time.sleep(1)

        if code is None:
            proc.kill()
            logfile.write('[  TIMEOUT  ] ')
            print(cmd + '\033[91m' + " TIMEOUT" + '\033[0m')
            return

        if code == 0:
            print(cmd + '\033[94m' + " PASS" + '\033[0m')
        else:
            logfile.write('[  FAILED  ] ')
            if code < 0:
                logfile.write('Terminated by signal: ' + str(-code) + '\n')
            else:
                logfile.write('Program returned ' + str(code) + '\n')
            print(cmd + '\033[91m' + " FAIL" + '\033[0m')



run_ut = env.Command(Dir('run_ut'), '', run_ut)
env.AlwaysBuild(run_ut)
env.Alias('agent:run_ut', run_ut)

# Local Variables:
# mode: python
# End:
